﻿/**
* \file lm.h
* \brief 莱温伯格·马夸尔特算法(Levenberg–Marquardt)
*
* \author huantai.xu@bozhon.com
* \date 2019-04-01
* \since 1.0.0.0
* \ingroup pcp_match
* \copyright (c) 2019,Beijing Bopixel Technology co.,Ltd, www.bopixel.com. All rights reserved.
*/
#pragma once

#pragma warning(disable: 4819)

#ifndef PCP_MATH_LM_H_
#define PCP_MATH_LM_H_
#pragma once
#include "opencv2/core/core.hpp"

#include <cstdio>
#include <vector>
#include <functional> 


//!函数类型Func为LM目标函数
typedef std::function<double(const cv::Mat& input, const cv::Mat& lms)> Func;

#pragma warning(disable: 4251)
#pragma warning(disable: 4819)

//#define INDEX_T(T) (sizeof(T) / 4 + 4)
/**
* \class LM
* \brief Levenberg–Marquardt算法,莱温伯格·马夸尔特算法。
*
* \attention 该算法对初始值敏感，初值设置需要一个相对较为准确的参数。
*/
class LM
{
public:
	LM();
	~LM();
public:

	/**
	* \brief 进行LM非线性最优解求解
	* \param[in] func		目标函数
	* \param[in] inputs		观测值(自变量)
	* \param[in] outputs	观测值(因变量)
	* \exception	DevidebyZeroException
	*/
	bool Compute(Func func, const cv::Mat& inputs, const cv::Mat& outputs);


	/**
	* \brief 设置初始参数
	* \param[in] params	初始参数(double，一行，且该行为一组有效参数)
	*/
	void InitParam(cv::Mat params);


	/**
	* \brief 设置最大迭代次数
	* \param[in]	maxIter 最大迭代次数
	*/
	void SetMaxIter(int maxIter);


	/**
	* \brief 设置初始步长
	* \param[in] step 迭代时的初始步长，置信域半径
	*/
	void SetDeriveStep(float step);


	/**
	* \brief 设置迭代终止条件-平均方差阈值
	* \param[in] prec 迭代时的终止精度，平均方差阈值
	*/
	void SetPrecision(float prec);


	/**
	* \brief 精度获得
	* \return double 迭代时的终止精度，平均方差
	*/
	float GetRealPrecision() const;


	/**
	* \brief 获取迭代终止时的迭代次数
	* \return int 迭代时的迭代次数
	*/
	int GetRealIter() const;


	/**
	* \brief 获取迭代结果
	* \return cv::Mat 结果向量(double，一行，且该行为一组有效参数)
	*/
	cv::Mat GetParams() const;

private:

	cv::Mat param_; //< 最优化参数

	int		max_iter_; //<最大迭代次数

	float	step_; //<置信域半径

	float	precision_; //<终止条件-精度

	int		real_iter_; //<算法终止时的迭代次数

	float	real_precision_; //<算法终止时的平均方差

	/**
	* \brief 用于计算雅可比矩阵
	*/
	float Deriv(Func func, const cv::Mat& input, const cv::Mat& lms, int n);

};

#endif //!PCP_MATH_LM_H_
